using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace ILSMultiPassLight
{

    public partial class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        KeyboardState currentKeys, oldKeys;

        Model droneModel;
        Texture2D droneTexture;
        Matrix droneWorld;

        Effect effect;
        Texture2D texture;
        Random rand;

        Matrix World, View, Projection;

        //Position of the camera
        Vector3 cameraPos = new Vector3(0, 60, 60);

        //Lists of lights
        List<DirectionalLight> dirLights;
        List<PointLight> pointLights;
        List<SpotLight> spotLights;

        SpriteBatch spriteBatch;
        SpriteFont font;

        //The on screen positions for the text to display
        Vector2 dirPos = new Vector2(5, 5), pointPos = new Vector2(5, 27), spotPos = new Vector2(5, 49), helpPos = new Vector2(5, 71);

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            dirLights = new List<DirectionalLight>();
            pointLights = new List<PointLight>();
            spotLights = new List<SpotLight>();

            rand = new Random();
        }

        protected override void Initialize()
        {
            base.Initialize();
        }


        protected override void LoadContent()
        {
            droneModel = Content.Load<Model>("L200-FBX");
            droneTexture = Content.Load<Texture2D>("truck_color-green");
            droneWorld = Matrix.CreateScale(0.05f) * Matrix.CreateTranslation(0f, 10f, 0f);



            effect = Content.Load<Effect>("MultiPassLight");
            texture = Content.Load<Texture2D>("grass");


            spriteBatch = new SpriteBatch(GraphicsDevice);
            font = Content.Load<SpriteFont>("Display");


            effect.Parameters["globalAmbient"].SetValue(Color.White.ToVector3());
            effect.Parameters["Ke"].SetValue(0.2f);
            effect.Parameters["Ka"].SetValue(0.2f);
            effect.Parameters["Kd"].SetValue(1.0f);
            effect.Parameters["Ks"].SetValue(0.3f);
            effect.Parameters["specularPower"].SetValue(100);
            effect.Parameters["spotPower"].SetValue(20);
            effect.Parameters["Texture"].SetValue(texture);


            World = Matrix.Identity;
            View = Matrix.CreateLookAt(cameraPos, Vector3.Zero, Vector3.Up);
            Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio, 1, 200);


            CreateVertexBuffer();
            CreateIndexBuffer();


            foreach (ModelMesh mesh in droneModel.Meshes)
            {
                foreach (ModelMeshPart meshPart in mesh.MeshParts)
                {
                    meshPart.Effect = effect;
                }
            }
            
        }


        protected override void UnloadContent()
        {
        }


        protected override void Update(GameTime gameTime)
        {
            currentKeys = Keyboard.GetState();

            HandleInput();

            oldKeys = currentKeys;

            //Update lights
            DirectionalLight.Update(dirLights);
            PointLight.Update(pointLights);
            SpotLight.Update(spotLights);


            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            GraphicsDevice.SetVertexBuffer(vertexBuffer);
            GraphicsDevice.Indices = indexBuffer;

            /*effect.Parameters["Texture"].SetValue(texture);
            effect.Parameters["WVP"].SetValue(World * View * Projection);
            effect.Parameters["World"].SetValue(World);
            effect.Parameters["eyePosition"].SetValue(Vector3.Transform(cameraPos, Matrix.Invert(World)));


            GraphicsDevice.BlendState = BlendState.Opaque;
            GraphicsDevice.DepthStencilState = DepthStencilState.Default;

            effect.CurrentTechnique.Passes["Ambient"].Apply();
            GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, number_of_vertices, 0, number_of_indices / 3);



            GraphicsDevice.BlendState = BlendState.Additive;
            foreach (DirectionalLight light in dirLights)
            {
                effect.Parameters["lightColor"].SetValue(light.Color.ToVector3());
                effect.Parameters["lightDirection"].SetValue(light.Direction);
                effect.CurrentTechnique.Passes["Directional"].Apply();
                GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, number_of_vertices, 0, number_of_indices / 3);
            }

            foreach (PointLight light in pointLights)
            {
                effect.Parameters["lightColor"].SetValue(light.Color.ToVector3());
                effect.Parameters["lightPosition"].SetValue(light.Position);
                effect.CurrentTechnique.Passes["Point"].Apply();
                GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, number_of_vertices, 0, number_of_indices / 3);
            }

            foreach (SpotLight light in spotLights)
            {
                effect.Parameters["lightColor"].SetValue(light.Color.ToVector3());
                effect.Parameters["lightPosition"].SetValue(light.Position);
                effect.Parameters["lightDirection"].SetValue(light.Direction);
                effect.CurrentTechnique.Passes["Spot"].Apply();
                GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, number_of_vertices, 0, number_of_indices / 3);
            } */


            effect.Parameters["WVP"].SetValue(droneWorld * View * Projection);
            effect.Parameters["World"].SetValue(droneWorld);
            effect.Parameters["eyePosition"].SetValue(Vector3.Transform(cameraPos, Matrix.Invert(droneWorld)));

            // Ab hier
            GraphicsDevice.BlendState = BlendState.Opaque;
            GraphicsDevice.DepthStencilState = DepthStencilState.Default;
            effect.CurrentTechnique.Passes["Ambient"].Apply();

            foreach (ModelMesh mesh in droneModel.Meshes)
            {
                foreach (ModelMeshPart meshPart in mesh.MeshParts)
                {
                    GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer, meshPart.VertexOffset);
                    GraphicsDevice.Indices = meshPart.IndexBuffer;
                    effect.Parameters["Texture"].SetValue(droneTexture);
                    meshPart.Effect = effect;
                    GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0,
                        meshPart.NumVertices, meshPart.StartIndex, meshPart.PrimitiveCount);
                }
            }

            GraphicsDevice.BlendState = BlendState.Additive;
            foreach (DirectionalLight light in dirLights)
            {
                effect.Parameters["lightColor"].SetValue(light.Color.ToVector3());
                effect.Parameters["lightDirection"].SetValue(light.Direction);
                effect.CurrentTechnique.Passes["Directional"].Apply();
                GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, number_of_vertices, 0, number_of_indices / 3);
                foreach (ModelMesh mesh in droneModel.Meshes)
                {
                    foreach (ModelMeshPart meshPart in mesh.MeshParts)
                    {
                        meshPart.Effect = effect;
                    }
                    mesh.Draw();
                }
            }

            foreach (PointLight light in pointLights)
            {
                effect.Parameters["lightColor"].SetValue(light.Color.ToVector3());
                effect.Parameters["lightPosition"].SetValue(light.Position);
                effect.CurrentTechnique.Passes["Point"].Apply();
                GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, number_of_vertices, 0, number_of_indices / 3);
                foreach (ModelMesh mesh in droneModel.Meshes)
                {
                    foreach (ModelMeshPart meshPart in mesh.MeshParts)
                    {
                        meshPart.Effect = effect;
                    }
                    mesh.Draw();
                }
            }

            foreach (SpotLight light in spotLights)
            {
                effect.Parameters["lightColor"].SetValue(light.Color.ToVector3());
                effect.Parameters["lightPosition"].SetValue(light.Position);
                effect.Parameters["lightDirection"].SetValue(light.Direction);
                effect.CurrentTechnique.Passes["Spot"].Apply();
                GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, number_of_vertices, 0, number_of_indices / 3);
                foreach (ModelMesh mesh in droneModel.Meshes)
                {
                    foreach (ModelMeshPart meshPart in mesh.MeshParts)
                    {
                        meshPart.Effect = effect;
                    }
                    mesh.Draw();
                }
            }

            spriteBatch.Begin();
            spriteBatch.DrawString(font, "Directional lights: " + dirLights.Count, dirPos, Color.Black);
            spriteBatch.DrawString(font, "Point lights: " + pointLights.Count, pointPos, Color.Black);
            spriteBatch.DrawString(font, "Spot lights: " + spotLights.Count, spotPos, Color.Black);
            spriteBatch.DrawString(font, "Q W E to add lights\nA S D to remove lights\nLeft Right to rotate camera", helpPos, Color.Black);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}
